package main

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

type Server struct {
	Ip   string
	Port int

	//在线用户map
	OnlineMap map[string]*User
	//读写锁
	mapLock sync.RWMutex

	//消息广播channel
	Message chan string
}

//创建一个server
func NewServer(ip string, port int) *Server {
	server := &Server{
		Ip:        ip,
		Port:      port,
		OnlineMap: make(map[string]*User),
		mapLock:   sync.RWMutex{},
		Message:   make(chan string),
	}
	return server
}

//创建一个业务handler
func (s *Server) Handler(conn net.Conn) {
	fmt.Println("链接成功!", conn.RemoteAddr())
	//创建新用户
	user := NewUser(conn, s)

	//广播当前用户上线消息
	user.Online()

	//监听当前用户是否活跃的channel
	isLive := make(chan bool)

	//处理实际业务
	//接收客户端的信息,将其广播给其他在线user
	go func() {
		buff := make([]byte, 2048)
		for {
			n, err := conn.Read(buff)
			if n == 0 {
				user.Offline()
				return
			}

			if err != nil && err != io.EOF {
				fmt.Println("服务器接收失败，error：", err)
				return
			}
			//提取用户消息，去除最后一个\n符
			msg := string(buff[:n-1])
			user.SendMsg(msg)
			isLive <- true
		}
	}()

	//当前handler必须阻塞，不然方法体运行结束，里面的user也消失
	for {
		select {
		case <-isLive: //如果有值说明活跃，不用做处理,写在最前，下面300秒的定时会判断，相当于定时器重置
		case <-time.After(300 * time.Second):
			//这里说明不活跃，强踢下线
			user.conn.Write([]byte("你不活跃，被踢了\n"))
			//channel 销毁
			close(user.C)
			//连接关闭
			user.conn.Close()

		}
	}

}

//监听Message channel,一旦有消息就广播给在线的user
func (s *Server) ListenMessager() {
	for {
		//取消息
		msg := <-s.Message
		//给在线的user发送消息,操作map要加锁，map不是线程安全的
		s.mapLock.Lock()
		for _, user := range s.OnlineMap {
			//将消息发给各自在线user的消息channel，user里有监听channel的消息并将其发送给客户端
			user.C <- msg
		}
		s.mapLock.Unlock()
	}
}

//广播消息，将消息注入Message channel中
func (s *Server) Broadcast(user *User, msg string) {
	sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
	s.Message <- sendMsg
}

//启动服务器
func (s *Server) Start() {
	//1 socket listen
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port))
	if err != nil {
		fmt.Println("net.Listen error：", err)
		return
	}
	//2 close socket listen
	defer listener.Close()

	//启动监听Message的goroutine
	go s.ListenMessager()

	for {
		//3 accept
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener.Accept error：", err)
			continue
		}
		//4 do handler 开启go程处理handler，继续监听
		go s.Handler(conn)
	}

}
